﻿using System;
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using System.Linq;
using UnityEditor.IMGUI.Controls;

namespace Hotfix
{
    /// <summary>
    /// 
    /// </summary>
    public class XHotfixAssetBundleTreeItem : TreeViewItem
    {   
        /// <summary>
        /// 
        /// </summary>
        private XHotfixBundleInfo bundleInfo;

        /// <summary>
        /// 
        /// </summary>
        internal XHotfixBundleInfo bundle
        {
            get { return bundleInfo; }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="b"></param>
        /// <param name="depth"></param>
        /// <param name="iconTexture"></param>
        public XHotfixAssetBundleTreeItem(XHotfixBundleInfo b, int depth, Texture2D iconTexture) : base(b.nameHashCode, depth, b.displayName)
        {
            bundleInfo = b;
            icon = iconTexture;
            children = new List<TreeViewItem>();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public XHotfixMessageSystem.Message BundleMessage()
        {
            return bundleInfo.HighestMessage();
        }

        /// <summary>
        /// 
        /// </summary>
        public override string displayName
        {
            get
            {
                return XHotfixAssetBundleBrowserMain.instance.m_ManageTab.hasSearch ? bundleInfo.bundleNameData.fullNativeName : bundleInfo.displayName;
            }
        }
    }

    /// <summary>
    /// 
    /// </summary>
    public class XHotfixBundleNameData
    {
        /// <summary>
        /// 
        /// </summary>
        private List<string> pathTokenList;

        /// <summary>
        /// 
        /// </summary>
        private string fullBundleName;
        private string bundleShortName;
        private string bundleVariantName;
        private string bundleFullNativeName;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="name"></param>
        public XHotfixBundleNameData(string name) 
        {
            SetName(name);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="path"></param>
        /// <param name="name"></param>
        public XHotfixBundleNameData(string path, string name)
        {
            string finalName = System.String.IsNullOrEmpty(path) ? "" : path + '/';
            finalName += name;

            SetName(finalName);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            return fullNativeName.GetHashCode();
        }

        /// <summary>
        /// 
        /// </summary>
        public string fullNativeName
        { 
            get { return bundleFullNativeName; } 
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="bundleName"></param>
        /// <param name="variantName"></param>
        public void SetBundleName(string bundleName, string variantName)
        {
            string name = bundleName;
            name += System.String.IsNullOrEmpty(variantName) ? "" : "." + variantName;

            SetName(name);
        }

        /// <summary>
        /// 
        /// </summary>
        public string bundleName
        {
            get { return fullBundleName; }
        }

        /// <summary>
        /// 
        /// </summary>
        public string shortName
        {
            get { return bundleShortName; }
        }

        /// <summary>
        /// 
        /// </summary>
        public string variant
        {
            get { return bundleVariantName; }
            set
            {
                bundleVariantName = value;
                bundleFullNativeName = fullBundleName;
                bundleFullNativeName += System.String.IsNullOrEmpty(bundleVariantName) ? "" : "." + bundleVariantName;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public List<string> pathTokens
        {
            get { return pathTokenList; }
            set
            {
                pathTokenList = value.GetRange(0, value.Count-1);
                SetShortName(value.Last());
                GenerateFullName();
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="name"></param>
        public void SetName(string name)
        {
            if(pathTokenList == null)
                pathTokenList = new List<string>();
            else
                pathTokenList.Clear();

            int indexOfSlash = name.IndexOf('/');
            int previousIndex = 0;

            while(indexOfSlash != -1)
            {
                pathTokenList.Add(name.Substring(previousIndex, (indexOfSlash - previousIndex)));
                previousIndex = indexOfSlash + 1;
                indexOfSlash = name.IndexOf('/', previousIndex);
            }

            SetShortName(name.Substring(previousIndex));
            GenerateFullName();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="inputName"></param>
        public void SetShortName(string inputName)
        {
            bundleShortName = inputName;

            int indexOfDot = bundleShortName.LastIndexOf('.');
            if (indexOfDot > -1)
            {
                bundleVariantName = bundleShortName.Substring(indexOfDot + 1);
                bundleShortName = bundleShortName.Substring(0, indexOfDot);
            }
            else
            {
                bundleVariantName = string.Empty;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="newToken"></param>
        /// <param name="indexFromBack"></param>
        public void PartialNameChange(string newToken, int indexFromBack)
        {
            if(indexFromBack == 0)
            {
                 SetShortName(newToken);
            }
            else if(indexFromBack-1 < pathTokenList.Count)
            {
                pathTokenList[pathTokenList.Count - indexFromBack] = newToken;
            }

            GenerateFullName();
        }

        /// <summary>
        /// 
        /// </summary>
        public void GenerateFullName()
        {
            fullBundleName = string.Empty;
            for(int i = 0; i < pathTokenList.Count; i++)
            {
                fullBundleName += pathTokenList[i];
                fullBundleName += '/';
            }

            fullBundleName += bundleShortName;
            bundleFullNativeName = fullBundleName;
            bundleFullNativeName += System.String.IsNullOrEmpty(bundleVariantName) ? "" : "." + bundleVariantName;
        }
    }

    /// <summary>
    /// 
    /// </summary>
    public abstract class XHotfixBundleInfo
    {
        protected XHotfixBundleFolderInfo parentFolderInfo;

        /// <summary>
        /// 
        /// </summary>
        protected bool folderDoneUpdating;
        protected bool folderDirty;

        /// <summary>
        /// 
        /// </summary>
        protected XHotfixMessageSystem.MessageState bundleMessages = new XHotfixMessageSystem.MessageState();
        protected XHotfixMessageSystem.Message cachedHighMessage;

        /// <summary>
        /// 
        /// </summary>
        public XHotfixBundleNameData bundleNameData;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="name"></param>
        /// <param name="parent"></param>
        public XHotfixBundleInfo(string name, XHotfixBundleFolderInfo parent)
        {
            bundleNameData = new XHotfixBundleNameData(name);
            parentFolderInfo = parent;
        }

        /// <summary>
        /// 
        /// </summary>
        public XHotfixBundleFolderInfo parent
        { 
            get { return parentFolderInfo; } 
        }

        /// <summary>
        /// 
        /// </summary>
        public virtual string displayName
        {
            get { return bundleNameData.shortName; }
        }

        /// <summary>
        /// 
        /// </summary>
        public virtual int nameHashCode
        {
            get { return bundleNameData.GetHashCode(); }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="depth"></param>
        /// <returns></returns>
        public abstract XHotfixAssetBundleTreeItem CreateTreeView(int depth);

        /// <summary>
        /// 
        /// </summary>
        public virtual void RefreshMessages()
        {
            RefreshEmptyStatus();
            RefreshDupeAssetWarning();

            var flag = bundleMessages.HighestMessageFlag();
            cachedHighMessage = XHotfixMessageSystem.GetMessage(flag);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public abstract bool RefreshEmptyStatus();

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public abstract bool RefreshDupeAssetWarning();

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public virtual XHotfixMessageSystem.Message HighestMessage()
        {
            if (cachedHighMessage == null)
                RefreshMessages();

            return cachedHighMessage;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="flag"></param>
        /// <returns></returns>
        public bool IsMessageSet(XHotfixMessageSystem.MessageFlag flag)
        {
            return bundleMessages.IsSet(flag);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="flag"></param>
        /// <param name="on"></param>
        public void SetMessageFlag(XHotfixMessageSystem.MessageFlag flag, bool on)
        {
            bundleMessages.SetFlag(flag, on);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public List<XHotfixMessageSystem.Message> GetMessages()
        {
            return bundleMessages.GetMessages();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public bool HasMessages()
        {
            return bundleMessages.HasMessages();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="newName"></param>
        /// <param name="reverseDepth"></param>
        /// <returns></returns>
        public virtual bool HandleRename(string newName, int reverseDepth)
        {
            if (reverseDepth == 0)
            {
                if (!parentFolderInfo.HandleChildRename(bundleNameData.shortName, newName))
                    return false;
            }

            bundleNameData.PartialNameChange(newName, reverseDepth);
            return true;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="isRootOfDelete"></param>
        /// <param name="forcedNewName"></param>
        /// <param name="forcedNewVariant"></param>
        public virtual void HandleDelete(bool isRootOfDelete, string forcedNewName="", string forcedNewVariant = "")
        {
            if(isRootOfDelete)
            {
                parentFolderInfo.HandleChildRename(bundleNameData.shortName, string.Empty);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        abstract public void RefreshAssetList();

        /// <summary>
        /// 
        /// </summary>
        /// <param name="node"></param>
        abstract public void AddAssetsToNode(XHotfixAssetTreeViewItem node);

        /// <summary>
        /// 
        /// </summary>
        abstract public void Update();

        /// <summary>
        /// 
        /// </summary>
        public virtual bool doneUpdating
        { 
            get { return folderDoneUpdating; }
        }

        /// <summary>
        /// 
        /// </summary>
        public virtual bool dirty
        {
            get { return folderDirty; }
        }

        /// <summary>
        /// 
        /// </summary>
        public void ForceNeedUpdate()
        {
            folderDoneUpdating = false;
            folderDirty = true;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="parentName"></param>
        /// <param name="newParent"></param>
        public abstract void HandleReparent(string parentName, XHotfixBundleFolderInfo newParent = null);

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public abstract List<XHotfixAssetBundleAssetInfo> GetDependencies();

        /// <summary>
        /// 
        /// </summary>
        /// <param name="search"></param>
        /// <returns></returns>
        public abstract bool DoesItemMatchSearch(string search);
    }

    public class XHotfixBundleDependencyInfo
    {
        public string m_BundleName;
        public List<XHotfixAssetBundleAssetInfo> m_FromAssets;
        public List<XHotfixAssetBundleAssetInfo> m_ToAssets;

        public XHotfixBundleDependencyInfo( string bundleName, XHotfixAssetBundleAssetInfo fromAsset, XHotfixAssetBundleAssetInfo toAsset )
        {
            m_BundleName = bundleName;
            m_FromAssets = new List<XHotfixAssetBundleAssetInfo>();
            m_FromAssets.Add( fromAsset );
            m_ToAssets = new List<XHotfixAssetBundleAssetInfo>();
            m_ToAssets.Add( toAsset );
        }
    }

    public class XHotfixBundleDataInfo : XHotfixBundleInfo
    {
        protected List<XHotfixAssetBundleAssetInfo> m_ConcreteAssets;
        protected List<XHotfixAssetBundleAssetInfo> m_DependentAssets;
        protected List<XHotfixBundleDependencyInfo> m_BundleDependencies;
        protected int m_ConcreteCounter;
        protected int m_DependentCounter;
        protected bool m_IsSceneBundle;
        protected long m_TotalSize;

        internal XHotfixBundleDataInfo(string name, XHotfixBundleFolderInfo parent) : base(name, parent)
        {
            m_ConcreteAssets = new List<XHotfixAssetBundleAssetInfo>();
            m_DependentAssets = new List<XHotfixAssetBundleAssetInfo>();
            m_BundleDependencies = new List<XHotfixBundleDependencyInfo>();
            m_ConcreteCounter = 0;
            m_DependentCounter = 0;
        }
        ~XHotfixBundleDataInfo()
        {
            foreach (var asset in m_DependentAssets)
            {
                XHotfixAssetBundleModel.UnRegisterAsset(asset, bundleNameData.fullNativeName);
            }
        }
        public override bool HandleRename(string newName, int reverseDepth)
        { 
            RefreshAssetList();
            if (!base.HandleRename(newName, reverseDepth))
                return false;
            XHotfixAssetBundleModel.MoveAssetToBundle(m_ConcreteAssets, bundleNameData.bundleName, bundleNameData.variant);
            return true;
        }
        public override void HandleDelete(bool isRootOfDelete, string forcedNewName="", string forcedNewVariant="")
        {
            RefreshAssetList();
            base.HandleDelete(isRootOfDelete);
            XHotfixAssetBundleModel.MoveAssetToBundle(m_ConcreteAssets, forcedNewName, forcedNewVariant);
        }

        public string TotalSize()
        {
            if (m_TotalSize == 0)
                return "--";
            return EditorUtility.FormatBytes(m_TotalSize);
        }

        public override void RefreshAssetList()
        {
            bundleMessages.SetFlag(XHotfixMessageSystem.MessageFlag.AssetsDuplicatedInMultBundles, false);
            bundleMessages.SetFlag(XHotfixMessageSystem.MessageFlag.SceneBundleConflict, false);
            bundleMessages.SetFlag(XHotfixMessageSystem.MessageFlag.DependencySceneConflict, false);

            m_ConcreteAssets.Clear();
            m_TotalSize = 0;
            m_IsSceneBundle = false;

            foreach (var asset in m_DependentAssets)
            {
                XHotfixAssetBundleModel.UnRegisterAsset(asset, bundleNameData.fullNativeName);
            }
            m_DependentAssets.Clear();
            m_BundleDependencies.Clear();
            
            bool assetInBundle = false;
            bool sceneError = false;
            var assets = XHotfixAssetBundleModel.DataSource.GetAssetPathsFromAssetBundle(bundleNameData.fullNativeName);
            foreach(var assetName in assets)
            {
                if (AssetDatabase.GetMainAssetTypeAtPath(assetName) == typeof(SceneAsset))
                {
                    m_IsSceneBundle = true;
                    if(assetInBundle)
                        sceneError = true;
                }
                else
                {
                    assetInBundle = true;
                    if (m_IsSceneBundle)
                        sceneError = true;
                }

                var bundleName = XHotfixAssetBundleModel.GetBundleName(assetName);
                if (System.String.IsNullOrEmpty(bundleName))  
                {
                    ///we get here if the current asset is only added due to being in an explicitly added folder
                    

                    var partialPath = assetName;
                    while(
                        !System.String.IsNullOrEmpty(partialPath) && 
                        partialPath != "Assets" &&
                        System.String.IsNullOrEmpty(bundleName))
                    {
                        partialPath = partialPath.Substring(0, partialPath.LastIndexOf('/'));
                        bundleName = XHotfixAssetBundleModel.GetBundleName(partialPath);
                    }
                    if(!System.String.IsNullOrEmpty(bundleName))
                    {
                        var folderAsset = XHotfixAssetBundleModel.CreateAsset(partialPath, bundleName);
                        folderAsset.isFolder = true;
                        if (m_ConcreteAssets.FindIndex(a => a.displayName == folderAsset.displayName) == -1)
                        {
                            m_ConcreteAssets.Add(folderAsset);
                        }

                        var newAsset = XHotfixAssetBundleModel.CreateAsset(assetName, folderAsset);
                        if (newAsset != null)
                        {
                            m_DependentAssets.Add(newAsset);
                            if (m_DependentAssets != null && m_DependentAssets.Count > 0)
                            {
                                var last = m_DependentAssets.Last();
                                if (last != null)
                                    m_TotalSize += last.fileSize;
                            }
                        }
                    }
                }
                else
                {
                    var newAsset = XHotfixAssetBundleModel.CreateAsset (assetName, bundleNameData.fullNativeName);
                    if (newAsset != null)
                    {
                        m_ConcreteAssets.Add(newAsset);
                        m_TotalSize += m_ConcreteAssets.Last().fileSize;
                        if (AssetDatabase.GetMainAssetTypeAtPath(assetName) == typeof(SceneAsset))
                        {
                            m_IsSceneBundle = true;
                            m_ConcreteAssets.Last().isScene = true;
                        }
                    }
                }
            }
            
            if(sceneError)
            {
                foreach (var asset in m_ConcreteAssets)
                {
                    if (asset.isFolder)
                    {
                        asset.SetMessageFlag(XHotfixMessageSystem.MessageFlag.DependencySceneConflict, true);
                        bundleMessages.SetFlag(XHotfixMessageSystem.MessageFlag.DependencySceneConflict, true);
                    }
                    else
                    {
                        asset.SetMessageFlag(XHotfixMessageSystem.MessageFlag.SceneBundleConflict, true);
                        bundleMessages.SetFlag(XHotfixMessageSystem.MessageFlag.SceneBundleConflict, true);
                    }
                }
            }


            m_ConcreteCounter = 0;
            m_DependentCounter = 0;
            folderDirty = true;
        }

        public override void AddAssetsToNode(XHotfixAssetTreeViewItem node)
        {
            foreach (var asset in m_ConcreteAssets)
                node.AddChild(new XHotfixAssetTreeViewItem(asset));

            foreach (var asset in m_DependentAssets)
            {
                if(!node.ContainsChild(asset))
                    node.AddChild(new XHotfixAssetTreeViewItem(asset));
            }

            folderDirty = false;
        }
        public List<XHotfixBundleDependencyInfo> GetBundleDependencies()
        {
            return m_BundleDependencies;
        }

        public override void Update()
        {
            int dependents = m_DependentAssets.Count;
            int bundleDep = m_BundleDependencies.Count;
            if(m_ConcreteCounter < m_ConcreteAssets.Count)
            {
                GatherDependencies(m_ConcreteAssets[m_ConcreteCounter]);
                m_ConcreteCounter++;
                folderDoneUpdating = false;
            }
            else if (m_DependentCounter < m_DependentAssets.Count)
            {
                GatherDependencies(m_DependentAssets[m_DependentCounter], bundleNameData.fullNativeName);
                m_DependentCounter++;
                folderDoneUpdating = false;
            }
            else
            {
                folderDoneUpdating = true;
            }
            folderDirty = (dependents != m_DependentAssets.Count) || (bundleDep != m_BundleDependencies.Count);
            if (folderDirty || folderDoneUpdating)
                RefreshMessages();
        }

        private void GatherDependencies(XHotfixAssetBundleAssetInfo asset, string parentBundle = "")
        {
            if (System.String.IsNullOrEmpty(parentBundle))
                parentBundle = asset.bundleName;

            if (asset == null)
                return;

            var deps = asset.GetDependencies();
            if (deps == null)
                return;

            foreach (var ai in deps)
            {
                if (ai == asset || m_ConcreteAssets.Contains(ai) || m_DependentAssets.Contains(ai))
                    continue;

                var bundleName = XHotfixAssetBundleModel.DataSource.GetImplicitAssetBundleName(ai.fullAssetName);
                if (string.IsNullOrEmpty(bundleName))
                {
                    m_DependentAssets.Add(ai);
                    m_TotalSize += ai.fileSize;
                    if (XHotfixAssetBundleModel.RegisterAsset(ai, parentBundle) > 1)
                    {
                        SetDuplicateWarning();
                    }
                }
                else if(bundleName != bundleNameData.fullNativeName)
                {
                    XHotfixBundleDependencyInfo dependencyInfo = m_BundleDependencies.Find( m => m.m_BundleName == bundleName );

                    if( dependencyInfo == null )
                    {
                        dependencyInfo = new XHotfixBundleDependencyInfo( bundleName, asset, ai );
                        m_BundleDependencies.Add( dependencyInfo );
                    }
                    else
                    {
                        dependencyInfo.m_FromAssets.Add( asset );
                        dependencyInfo.m_ToAssets.Add( ai );
                    }
                }
            }
        }

        public override bool RefreshDupeAssetWarning()
        {
            foreach(var asset in m_DependentAssets)
            {
                if (asset != null && asset.IsMessageSet(XHotfixMessageSystem.MessageFlag.AssetsDuplicatedInMultBundles)) 
                {
                    SetDuplicateWarning();
                    return true;
                }
            }
            return false;
        }

        public bool IsEmpty()
        {
            return (m_ConcreteAssets.Count == 0);
        }

        public override bool RefreshEmptyStatus()
        {
            bool empty = IsEmpty();
            bundleMessages.SetFlag(XHotfixMessageSystem.MessageFlag.EmptyBundle, empty);
            return empty;
        }

        protected void SetDuplicateWarning()
        {
            bundleMessages.SetFlag(XHotfixMessageSystem.MessageFlag.AssetsDuplicatedInMultBundles, true);
            folderDirty = true;
        }

        internal bool isSceneBundle
        { get { return m_IsSceneBundle; } }

        public override XHotfixAssetBundleTreeItem CreateTreeView(int depth)
        {
            RefreshAssetList();
            RefreshMessages();
            if (isSceneBundle)
                return new XHotfixAssetBundleTreeItem(this, depth, XHotfixAssetBundleModel.GetSceneIcon());
            else
                return new XHotfixAssetBundleTreeItem(this, depth, XHotfixAssetBundleModel.GetBundleIcon());
        }

        public override void HandleReparent(string parentName, XHotfixBundleFolderInfo newParent = null)
        {
            RefreshAssetList();
            string newName = System.String.IsNullOrEmpty(parentName) ? "" : parentName + '/';
            newName += bundleNameData.shortName;
            if (newName == bundleNameData.bundleName)
                return;

            if (newParent != null && newParent.GetChild(newName) != null)
            {
                XHotfixAssetBundleModel.LogWarning("An item named '" + newName + "' already exists at this level in hierarchy.  If your desire is to merge bundles, drag one on top of the other.");
                return;
            }
            
            foreach (var asset in m_ConcreteAssets)
            {
                XHotfixAssetBundleModel.MoveAssetToBundle(asset, newName, bundleNameData.variant);
            }

            if (newParent != null)
            {
                parentFolderInfo.HandleChildRename(bundleNameData.shortName, string.Empty);
                parentFolderInfo = newParent;
                parentFolderInfo.AddChild(this);
            }
            bundleNameData.SetBundleName(newName, bundleNameData.variant);
        }

        public override List<XHotfixAssetBundleAssetInfo> GetDependencies()
        {
            return m_DependentAssets;
        }

        public override bool DoesItemMatchSearch(string search)
        {
            foreach(var asset in m_ConcreteAssets)
            {
                if (asset.displayName.IndexOf(search, StringComparison.OrdinalIgnoreCase) >= 0)
                    return true;
            }
            foreach (var asset in m_DependentAssets)
            {
                if (asset.displayName.IndexOf(search, StringComparison.OrdinalIgnoreCase) >= 0)
                    return true;
            }
            return false;
        }
    }

    public class XHotfixBundleVariantDataInfo : XHotfixBundleDataInfo
    {
        protected List<XHotfixAssetBundleAssetInfo> m_FolderIncludeAssets = new List<XHotfixAssetBundleAssetInfo>();
        internal XHotfixBundleVariantDataInfo(string name, XHotfixBundleFolderInfo parent) : base(name, parent)
        {
        }

        public override string displayName
        {
            get { return bundleNameData.variant; }
        }
        public override void Update()
        {
            base.Update();
            (parentFolderInfo as XHotfixBundleVariantFolderInfo).ValidateVariants();
        }
        public override void RefreshAssetList()
        {
            m_FolderIncludeAssets.Clear();
            base.RefreshAssetList();
            if(m_DependentAssets.Count > 0)
                m_FolderIncludeAssets = new List<XHotfixAssetBundleAssetInfo>(m_DependentAssets);
        }
        public bool IsSceneVariant()
        {
            RefreshAssetList();
            return isSceneBundle;
        }
        public override bool HandleRename(string newName, int reverseDepth)
        {
            if (reverseDepth == 0)
            {
                RefreshAssetList();
                if (!parentFolderInfo.HandleChildRename(bundleNameData.variant, newName))
                    return false;
                bundleNameData.variant = newName;
                XHotfixAssetBundleModel.MoveAssetToBundle(m_ConcreteAssets, bundleNameData.bundleName, bundleNameData.variant);
            }
            else if (reverseDepth == 1)
            {
                RefreshAssetList();
                bundleNameData.PartialNameChange(newName + "." + bundleNameData.variant, 0);
                XHotfixAssetBundleModel.MoveAssetToBundle(m_ConcreteAssets, bundleNameData.bundleName, bundleNameData.variant);
            }
            else
            {
                return base.HandleRename(newName, reverseDepth-1);
            }
            return true;
        }
        public override void HandleDelete(bool isRootOfDelete, string forcedNewName = "", string forcedNewVariant = "")
        {
            RefreshAssetList();
            if (isRootOfDelete)
            {
                parentFolderInfo.HandleChildRename(bundleNameData.variant, string.Empty);
            }
            XHotfixAssetBundleModel.MoveAssetToBundle(m_ConcreteAssets, forcedNewName, forcedNewVariant);
        }

        public bool FindContentMismatch(XHotfixBundleVariantDataInfo other)
        {
            bool result = false;

            if (m_FolderIncludeAssets.Count != 0 || other.m_FolderIncludeAssets.Count != 0)
            {
                var myUniqueAssets = new HashSet<string>();
                var otherUniqueAssets = new HashSet<string>(other.m_FolderIncludeAssets.Select(x => x.displayName));

                foreach (var asset in m_FolderIncludeAssets)
                {
                    if (!otherUniqueAssets.Remove(asset.displayName))
                    {
                        myUniqueAssets.Add(asset.displayName);
                    }
                }

                if (myUniqueAssets.Count > 0)
                {
                    bundleMessages.SetFlag(XHotfixMessageSystem.MessageFlag.VariantBundleMismatch, true);
                    result = true;
                }
                if (otherUniqueAssets.Count > 0)
                {
                    other.bundleMessages.SetFlag(XHotfixMessageSystem.MessageFlag.VariantBundleMismatch, true);
                    result = true;
                }
            }
            else //this doesn't cover the super weird case of including a folder and some explicit assets. TODO - fix that.
            {
                var myUniqueAssets = new HashSet<string>();
                var otherUniqueAssets = new HashSet<string>(other.m_ConcreteAssets.Select(x => x.displayName));

                foreach (var asset in m_ConcreteAssets)
                {
                    if (!otherUniqueAssets.Remove(asset.displayName))
                    {
                        myUniqueAssets.Add(asset.displayName);
                    }
                }

                if (myUniqueAssets.Count > 0)
                {
                    bundleMessages.SetFlag(XHotfixMessageSystem.MessageFlag.VariantBundleMismatch, true);
                    result = true;
                }
                if (otherUniqueAssets.Count > 0)
                {
                    other.bundleMessages.SetFlag(XHotfixMessageSystem.MessageFlag.VariantBundleMismatch, true);
                    result = true;
                }
            }
            return result;
        }
    }

    /// <summary>
    /// 
    /// </summary>
    public abstract class XHotfixBundleFolderInfo : XHotfixBundleInfo
    {
        protected Dictionary<string, XHotfixBundleInfo> children;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="name"></param>
        /// <param name="parent"></param>
        public XHotfixBundleFolderInfo(string name, XHotfixBundleFolderInfo parent) 
            : base(name, parent)
        {
            children = new Dictionary<string, XHotfixBundleInfo>();
        }

        public XHotfixBundleFolderInfo(List<string> path, int depth, XHotfixBundleFolderInfo parent) : base("", parent)
        {
            children = new Dictionary<string, XHotfixBundleInfo>();
            bundleNameData = new XHotfixBundleNameData("");
            bundleNameData.pathTokens = path.GetRange(0, depth);
        }

        public XHotfixBundleInfo GetChild(string name)
        {
            if (name == null)
                return null;

            XHotfixBundleInfo info = null;
            if (children.TryGetValue(name, out info))
                return info;
            return null;
        }
        public Dictionary<string, XHotfixBundleInfo>.ValueCollection GetChildList()
        {
            return children.Values;
        }
        public abstract void AddChild(XHotfixBundleInfo info);

        public override bool HandleRename(string newName, int reverseDepth)
        {
            if (!base.HandleRename(newName, reverseDepth))
                return false;

            foreach (var child in children)
            {
                child.Value.HandleRename(newName, reverseDepth + 1);
            }
            return true;
        }

        public override void HandleDelete(bool isRootOfDelete, string forcedNewName="", string forcedNewVariant = "")
        {
            base.HandleDelete(isRootOfDelete);
            foreach (var child in children)
            {
                child.Value.HandleDelete(false, forcedNewName, forcedNewVariant);
            }
            children.Clear();
        }

        public override bool DoesItemMatchSearch(string search)
        {
            return false; //folders don't ever match.
        }

        public override void RefreshMessages()
        {
            bundleMessages.SetFlag(XHotfixMessageSystem.MessageFlag.ErrorInChildren, false);
            foreach(var child in children)
            {
                if (child.Value.IsMessageSet(XHotfixMessageSystem.MessageFlag.Error))
                {
                    bundleMessages.SetFlag(XHotfixMessageSystem.MessageFlag.ErrorInChildren, true);
                    break;
                }
            }
            base.RefreshMessages();
        }
        public override bool RefreshEmptyStatus()
        {
            bool empty = children.Count == 0;
            foreach (var child in children)
            {
                empty |= child.Value.RefreshEmptyStatus();
            }
            bundleMessages.SetFlag(XHotfixMessageSystem.MessageFlag.EmptyFolder, empty);
            return empty;
        }

        public override void RefreshAssetList()
        {
            foreach (var child in children)
            {
                child.Value.RefreshAssetList();
            }
        }
        public override bool RefreshDupeAssetWarning()
        {
            bool dupeWarning = false;
            foreach (var child in children)
            {
                dupeWarning |= child.Value.RefreshDupeAssetWarning();
            }
            bundleMessages.SetFlag(XHotfixMessageSystem.MessageFlag.WarningInChildren, dupeWarning);
            return dupeWarning;
        }
        public override void AddAssetsToNode(XHotfixAssetTreeViewItem node)
        {
            foreach (var child in children)
            {
                child.Value.AddAssetsToNode(node);
            }
            folderDirty = false;
        }
        public virtual bool HandleChildRename(string oldName, string newName)
        {

            if (!System.String.IsNullOrEmpty(newName) && children.ContainsKey(newName))
            {
                XHotfixAssetBundleModel.LogWarning("Attempting to name an item '" + newName + "' which matches existing name at this level in hierarchy.  If your desire is to merge bundles, drag one on top of the other.");
                return false;
            }

            XHotfixBundleInfo info = null;
            if (children.TryGetValue(oldName, out info))
            {
                children.Remove(oldName);
                if (!System.String.IsNullOrEmpty(newName))
                    children.Add(newName, info);
            }
            return true;
        }

        public override void Update()
        {
            folderDirty = false;
            folderDoneUpdating = true;
            foreach (var child in children)
            {
                child.Value.Update();
                folderDirty |= child.Value.dirty;
                folderDoneUpdating &= child.Value.doneUpdating;
            }

            if (folderDirty || folderDoneUpdating)
                RefreshMessages();
        }
        public override bool doneUpdating
        {
            get
            {
                foreach (var child in children)
                {
                    folderDoneUpdating &= child.Value.doneUpdating;
                }
                return base.doneUpdating;
            }
        }


        public override List<XHotfixAssetBundleAssetInfo> GetDependencies()
        {
            List<XHotfixAssetBundleAssetInfo> assets = new List<XHotfixAssetBundleAssetInfo>();
            foreach (var child in children)
            {
                assets.AddRange(child.Value.GetDependencies());
            }
            return assets;
        }
    }

    public class XHotfixBundleFolderConcreteInfo : XHotfixBundleFolderInfo
    {
        public XHotfixBundleFolderConcreteInfo(string name, XHotfixBundleFolderInfo parent) 
            : base(name, parent)
        {
        }

        public XHotfixBundleFolderConcreteInfo(List<string> path, int depth, XHotfixBundleFolderInfo parent) 
            : base(path, depth, parent)
        {
        }

        public override void AddChild(XHotfixBundleInfo info)
        {
            children.Add(info.displayName, info);
        }
        public override XHotfixAssetBundleTreeItem CreateTreeView(int depth)
        {
            RefreshMessages();
            var result = new XHotfixAssetBundleTreeItem(this, depth, XHotfixAssetBundleModel.GetFolderIcon());
            foreach (var child in children)
            {
                result.AddChild(child.Value.CreateTreeView(depth + 1));
            }
            return result;
        }
        public override void HandleReparent(string parentName, XHotfixBundleFolderInfo newParent = null)
        {
            string newName = System.String.IsNullOrEmpty(parentName) ? "" : parentName + '/';
            newName += displayName;
            if (newName == bundleNameData.bundleName)
                return;

            if (newParent != null && newParent.GetChild(newName) != null)
            {
                XHotfixAssetBundleModel.LogWarning("An item named '" + newName + "' already exists at this level in hierarchy.  If your desire is to merge bundles, drag one on top of the other.");
                return;
            }

            foreach (var child in children)
            {
                child.Value.HandleReparent(newName);
            }

            if (newParent != null)
            {
                parentFolderInfo.HandleChildRename(bundleNameData.shortName, string.Empty);
                parentFolderInfo = newParent;
                parentFolderInfo.AddChild(this);
            }
            bundleNameData.SetBundleName(newName, bundleNameData.variant);
        }
    }


    public class XHotfixBundleVariantFolderInfo : XHotfixBundleFolderInfo
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="name"></param>
        /// <param name="parent"></param>
        public XHotfixBundleVariantFolderInfo(string name, XHotfixBundleFolderInfo parent) 
            : base(name, parent)
        {
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="info"></param>
        public override void AddChild(XHotfixBundleInfo info)
        {
            children.Add(info.bundleNameData.variant, info);
        }

        private bool m_validated;
        public override void Update()
        {
            m_validated = false;
            base.Update();
            if(!m_validated)
               ValidateVariants();
        }
        public void ValidateVariants()
        {
            m_validated = true;
            bool childMismatch = false;
            if(children.Count > 1)
            {
                XHotfixBundleVariantDataInfo goldChild = null;
                foreach(var c in children)
                {
                    var child = c.Value as XHotfixBundleVariantDataInfo;
                    child.SetMessageFlag(XHotfixMessageSystem.MessageFlag.VariantBundleMismatch, false);
                    if (goldChild == null)
                    {
                        goldChild = child;
                        continue;
                    }
                    childMismatch |= goldChild.FindContentMismatch(child);
                }
            }
            bundleMessages.SetFlag(XHotfixMessageSystem.MessageFlag.VariantBundleMismatch, childMismatch);

        }

        public override XHotfixAssetBundleTreeItem CreateTreeView(int depth)
        {
            RefreshMessages();
            Texture2D icon = null;
            if ((children.Count > 0) &&
                ((children.First().Value as XHotfixBundleVariantDataInfo).IsSceneVariant()))
            {
                icon = XHotfixAssetBundleModel.GetSceneIcon();
            }
            else
                icon = XHotfixAssetBundleModel.GetBundleIcon();

            var result = new XHotfixAssetBundleTreeItem(this, depth, icon);
            foreach (var child in children)
            {
                result.AddChild(child.Value.CreateTreeView(depth + 1));
            }
            return result;
        }

        public override void HandleReparent(string parentName, XHotfixBundleFolderInfo newParent = null)
        {
            string newName = System.String.IsNullOrEmpty(parentName) ? "" : parentName + '/';
            newName += displayName;
            if (newName == bundleNameData.bundleName)
                return;

            if (newParent != null && newParent.GetChild(newName) != null)
            {
                XHotfixAssetBundleModel.LogWarning("An item named '" + newName + "' already exists at this level in hierarchy.  If your desire is to merge bundles, drag one on top of the other.");
                return;
            }

            foreach (var child in children)
            {
                child.Value.HandleReparent(parentName);
            }

            if (newParent != null)
            {
                parentFolderInfo.HandleChildRename(bundleNameData.shortName, string.Empty);
                parentFolderInfo = newParent;
                parentFolderInfo.AddChild(this);
            }
            bundleNameData.SetBundleName(newName, string.Empty) ;
        }
        public override bool HandleChildRename(string oldName, string newName)
        {
            var result = base.HandleChildRename(oldName, newName);
            if (children.Count == 0)
                HandleDelete(true);
            return result;
        }
    }

}
